from __future__ import absolute_import

from builtins import bytes
from builtins import range

import functools
import inspect
import numbers
import struct
import sys

import lldb
import six

from .compat import valueint as int

__all__ = []

UPCASTS = {}

#
# List of Quirks, once fixed, replace the booleans with an evaluation
# of whether the lldb being used has the fix or not.
#

#
# rdar://99785324 (GetValueAsUnsigned performs sign extension
#                  when it REALLY shouldn't on bitfields)
#
QUIRK_99785324 = True

#
# rdar://99806493 (SBValue.Cast() is not doing the right thing with PAC)
#
QUIRK_99806493 = True

#
# rdar://100103405 (Default value for target.prefer-dynamic-value makes
#                  macros 40x as slow when connected to Astris)
#
QUIRK_100103405 = True

#
# rdar://100162262 ([correctness] Multiple dereferences do not result
#                  in the same load address in some cases depending
#                  on whether the original value is a pointer created
#                  with AddressOf() or not.)
#
QUIRK_100162262 = True

#
# rdar://102642763 (LLDB macros are unable to access member of anon struct
#                  or union in C++ class)
#
QUIRK_102642763 = True

#
# rdar://104494282 (Lldb computes correct "Value As Address" but Dereference()s
#                   wrong)
#
QUIRK_104494282 = True


I8_STRUCT  = struct.Struct('b')
I16_STRUCT = struct.Struct('h')
I32_STRUCT = struct.Struct('i')
I64_STRUCT = struct.Struct('q')

U8_STRUCT  = struct.Struct('B')
U16_STRUCT = struct.Struct('H')
U32_STRUCT = struct.Struct('I')
U64_STRUCT = struct.Struct('Q')

FLT_STRUCT = struct.Struct('f')
DBL_STRUCT = struct.Struct('d')


def lldbwrap_raise(exn, fn, reason, *args, **kwargs):
    """
    Helper to form a helpful exception string for the generic lldb.SB*
    checked wrappers

    @param exn (Exception type)
        The type of exception to raise

    @param fn (Function)
        The function that failed (approximately)

    @param reason (string)
        A reason string to append

    @params *args, **kwargs
        The arguments that have been passed to @c fn
        in order to pretty pring them in something useful
    """
    args_str = []

    for arg in args:
        if isinstance(arg, lldb.SBValue):
            args_str.append("<lldb.SBValue ({} &){:#x}>".format(
                lldb.SBValue(arg).GetType().GetDisplayTypeName(),
                lldb.SBValue(arg).GetLoadAddress())
            )
        elif isinstance(arg, lldb.SBType):
            args_str.append("<lldb.SBType {}>".format(lldb.SBType(arg).GetDisplayTypeName()))
        elif isinstance(arg, numbers.Integral):
            args_str.append("{:#x}".format(arg))
        else:
            args_str.append(repr(arg))

    if len(kwargs) > 0:
        args_str.append("...")

    if reason:
        raise exn("{}({}) failed: {}".format(
            fn.__name__, ", ".join(args_str), reason))
    raise exn("{}({}) failed".format(fn.__name__, ", ".join(args_str)))


def lldbwrap_update_class_dict(basename, basecls, attr):
    """
    Make the extension dictionary for our synthesized classes

    This function will add wrappers around certain functions
    that will inspect their return type, and when it is
    of one of the @c UPCASTS ones, will monkey patch
    the return value __class__.

    It would be cleaner to invoke a "copy constructor", however
    it has a very high cost, so this brittle monkey patching
    is used instead.
    """

    def _make_upcast_wrapper(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            result = fn(*args, **kwargs)
            upcast = UPCASTS.get(result.__class__)
            if upcast: upcast(result)
            return result

        return wrapper

    def _make_checked_upcast_wrapper(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            result = fn(*args, **kwargs)
            upcast = UPCASTS.get(result.__class__)
            if not upcast:
                return result
            if result.IsValid():
                upcast(result)
                return result
            lldbwrap_raise(ValueError, fn, None, *args, **kwargs)

        return wrapper

    @classmethod
    def xUpcast(cls, value):
        value.__class__ = cls

    #
    # Those methods return scalars, and are very popular
    # wrapping only makes them slow with no benefit.
    #
    DO_NOT_WRAP = set([
        'GetByteSize',
        'GetAddressByteSize',
        'GetLoadAddress',
        'GetName',
        'GetOffsetInBits',
        'GetOffsetInBytes',
        'GetStopID',
        'GetTypeFlags',
        'GetUniqueID',
        'GetValueAsAddress',
        'GetValueAsSigned',
        'GetValueAsUnsigned',
        'TypeIsPointerType',
        'IsValid',
    ])

    DO_NOT_WRAP_PREFIX = [
        '__',           # do not wrap magic python functions
        'Is',           # LLDB's "Is*" APIs return booleans
        'GetNum',       # LLDB's "GetNum*" APIs return integers
        'GetIndex',     # LLDB's "GetIndex*" APIs return integers
    ]

    for fname, value in inspect.getmembers(basecls):
        if fname in DO_NOT_WRAP:
            continue

        elif any(fname.startswith(pfx) for pfx in DO_NOT_WRAP_PREFIX):
            continue

        elif inspect.isfunction(value) or inspect.ismethod(value):
            attr.setdefault(fname, _make_upcast_wrapper(value))
            attr.setdefault('chk' + fname, _make_checked_upcast_wrapper(value))
            attr.setdefault('raw' + fname, value)

        elif isinstance(value, property):
            attr[fname] = property(_make_upcast_wrapper(value.fget), value.fset, doc=value.__doc__)

    attr.setdefault('xUpcast', xUpcast)


class LLDBWrapMetaclass(type):
    """ Metaclass used for manual definitions of lldb.SB* subclasses """

    def __new__(cls, name, bases, attr):
        lldbwrap_update_class_dict(name, bases[0], attr)
        return type.__new__(cls, name, bases, attr)


@six.add_metaclass(LLDBWrapMetaclass)
class SBProcess(lldb.SBProcess):

    #
    # Manually written checked wrappers
    #

    @functools.wraps(lldb.SBProcess.ReadMemory)
    def chkReadMemory(self, addr, size):
        err = lldb.SBError()
        res = self.ReadMemory(addr, size, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.ReadMemory, err.GetCString(),
            self, addr, size)

    @functools.wraps(lldb.SBProcess.WriteMemory)
    def chkWriteMemory(self, addr, buf):
        err = lldb.SBError()
        res = self.WriteMemory(addr, buf, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.WriteMemory, err.GetCString(),
            self, addr, buf)

    @functools.wraps(lldb.SBProcess.ReadCStringFromMemory)
    def chkReadCStringFromMemory(self, addr, max_size):
        err = lldb.SBError()
        res = self.ReadCStringFromMemory(addr, max_size, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.ReadCStringFromMemory, err.GetCString(),
            self, addr, max_size)

    @functools.wraps(lldb.SBProcess.ReadUnsignedFromMemory)
    def chkReadUnsignedFromMemory(self, addr, byte_size):
        err = lldb.SBError()
        res = self.ReadUnsignedFromMemory(addr, byte_size, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.ReadUnsignedFromMemory, err.GetCString(),
            self, addr, byte_size)

    @functools.wraps(lldb.SBProcess.ReadPointerFromMemory)
    def chkReadPointerFromMemory(self, addr):
        err = lldb.SBError()
        res = self.ReadPointerFromMemory(addr, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.ReadPointerFromMemory, err.GetCString(),
            self, addr)


@six.add_metaclass(LLDBWrapMetaclass)
class SBTarget(lldb.SBTarget):

    #
    # Manually written checked wrappers
    #

    @functools.wraps(lldb.SBTarget.ReadMemory)
    def chkReadMemory(self, addr, buf):
        err = lldb.SBError()
        res = self.ReadMemory(addr, buf, err)
        if err.Success():
            return res
        lldbwrap_raise(IOError, self.ReadMemory, err.GetCString(),
            self, addr, buf)


    #
    # Extensions
    #

    def xReadBytes(self, addr, size):
        """
        Reads memory from the current process's address space and removes any
        traps that may have been inserted into the memory.

        @param addr (int)
            The address to start reading at

        @param size (int)
            The size of the read to perform

        @returns (bytes)
        """
        return bytes(self.GetProcess().chkReadMemory(addr, size))

    def xReadCString(self, addr, max_size):
        """
        Reads a NULL terminated C string from the current process's address space.
        It returns a python string of the exact length, or truncates the string if
        the maximum character limit is reached. Example: ::

        @param addr (int)
            The address to start reading at

        @param max_size (int)
            The maximum size of the string

        @returns (str)
        """

        return self.GetProcess().chkReadCStringFromMemory(addr, max_size)

    def xReadInt8(self, addr):
        """ Conveniency wrapper to read an int8_t at the specified address """
        return int(I8_STRUCT.unpack(self.xReadBytes(addr, 1))[0])

    def xReadInt16(self, addr):
        """ Conveniency wrapper to read an int16_t at the specified address """
        return int(I16_STRUCT.unpack(self.xReadBytes(addr, 2))[0])

    def xReadInt32(self, addr):
        """ Conveniency wrapper to read an int32_t at the specified address """
        return int(I32_STRUCT.unpack(self.xReadBytes(addr, 4))[0])

    def xReadInt64(self, addr):
        """ Conveniency wrapper to read an int64_t at the specified address """
        return int(I64_STRUCT.unpack(self.xReadBytes(addr, 8))[0])

    def xReadUInt8(self, addr):
        """ Conveniency wrapper to read an uint8_t at the specified address """
        return int(U8_STRUCT.unpack(self.xReadBytes(addr, 1))[0])

    def xReadUInt16(self, addr):
        """ Conveniency wrapper to read an uint16_t at the specified address """
        return int(U16_STRUCT.unpack(self.xReadBytes(addr, 2))[0])

    def xReadUInt32(self, addr):
        """ Conveniency wrapper to read an uint32_t at the specified address """
        return int(U32_STRUCT.unpack(self.xReadBytes(addr, 4))[0])

    def xReadUInt64(self, addr):
        """ Conveniency wrapper to read an uint64_t at the specified address """
        return int(U64_STRUCT.unpack(self.xReadBytes(addr, 8))[0])

    def xReadLong(self, addr):
        """ Conveniency wrapper to read a long at the specified address """
        if self.GetProcess().GetAddressByteSize() == 8:
            return int(I64_STRUCT.unpack(self.xReadBytes(addr, 8))[0])
        return int(I32_STRUCT.unpack(self.xReadBytes(addr, 4))[0])

    def xReadULong(self, addr):
        """ Conveniency wrapper to read a long at the specified address """
        if self.GetProcess().GetAddressByteSize() == 8:
            return int(U64_STRUCT.unpack(self.xReadBytes(addr, 8))[0])
        return int(U32_STRUCT.unpack(self.xReadBytes(addr, 4))[0])

    def xReadFloat(self, addr):
        """ Conveniency wrapper to read a float at the specified address """
        return FLT_STRUCT.unpack(self.xReadBytes(addr, 4))[0]

    def xReadDouble(self, addr):
        """ Conveniency wrapper to read a double at the specified address """
        return DBL_STRUCT.unpack(self.xReadBytes(addr, 8))[0]


    def xIterAsStruct(self, spec, addr, count):
        """
        Iterate the memory as defined by the specified struct spec

        @param spec (struct.Struct)
            A struct unpack spec

        @param addr (int)
            The address to start ieterating from

        @param count (int)
            The number of structs to read
        """

        if not count:
            return ()

        size = spec.size
        data = self.xReadBytes(addr, count * size)
        if hasattr(spec, 'iter_unpack'):
            return spec.iter_unpack(data)

        # Python 2
        return (
            spec.unpack(data[i : i + size])
            for i in range(0, count * size, size)
        )


    def xIterAsScalar(self, spec, addr, count):
        """
        Iterate the memory as defined by the specified scalar spec

        Unlike xIterAsStruct() this will return the first element
        of the struct.Strict.iter_unpack() tuple.

        @param spec (struct.Struct)
            A struct unpack spec

        @param addr (int)
            The address to start ieterating from

        @param count (int)
            The number of scalars to read
        """

        if not count:
            return ()

        size = spec.size
        data = self.xReadBytes(addr, count * size)
        if hasattr(spec, 'iter_unpack'):
            return (e[0] for e in spec.iter_unpack(data))

        # Python 2
        return (
            int(spec.unpack(data[i : i + size])[0])
            for i in range(0, count * size, size)
        )

    def xIterAsInt8(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on int8_t """
        return self.xIterAsScalar(I8_STRUCT, addr, count)

    def xIterAsInt16(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on int16_t """
        return self.xIterAsScalar(I16_STRUCT, addr, count)

    def xIterAsInt32(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on int32_t """
        return self.xIterAsScalar(I32_STRUCT, addr, count)

    def xIterAsInt64(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on int64_t """
        return self.xIterAsScalar(I64_STRUCT, addr, count)

    def xIterAsUInt8(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on uint8_t """
        return self.xIterAsScalar(U8_STRUCT, addr, count)

    def xIterAsUInt16(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on uint16_t """
        return self.xIterAsScalar(U16_STRUCT, addr, count)

    def xIterAsUInt32(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on uint32_t """
        return self.xIterAsScalar(U32_STRUCT, addr, count)

    def xIterAsUInt64(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on uint64_t """
        return self.xIterAsScalar(U64_STRUCT, addr, count)

    def xIterAsLong(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on long """
        if self.GetProcess().GetAddressByteSize() == 8:
            return self.xIterAsScalar(I64_STRUCT, addr, count)
        return self.xIterAsScalar(I32_STRUCT, addr, count)

    def xIterAsULong(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on unsigned long """
        if self.GetProcess().GetAddressByteSize() == 8:
            return self.xIterAsScalar(U64_STRUCT, addr, count)
        return self.xIterAsScalar(U32_STRUCT, addr, count)

    def xIterAsFloat(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on float """
        return self.xIterAsScalar(FLT_STRUCT, addr, count)

    def xIterAsDouble(self, addr, count):
        """ Conveniency wrapper to xIterAsScalar() on double """
        return self.xIterAsScalar(DBL_STRUCT, addr, count)


    def xCreateValueFromAddress(self, name, addr, ty):
        """
        Create an SBValue with the given name by treating the memory starting
        at addr as an entity of type.

        More tolerant wrapper around CreateValueFromAddress() that accepts
        for @c name to be None and @c addr to be an int.

        @param name (str or None)
            The name of the resultant SBValue

        @param addr (int or lldb.SBAddress)
            The address of the start of the memory region to be used.

        @param ty (lldb.SBType)
            The type to use to interpret the memory starting at addr.

        @return (lldb.SBValue)
            An SBValue of the given type.

        @raises ValueError
            For various error conditions.
        """

        if not isinstance(addr, lldb.SBAddress):
            addr = self.rawResolveLoadAddress(addr)

        if name is None:
            # unlike SBValue's variant, SBTargets's will fail to produce
            # a value if the name is None, don't ask.
            name = 'newvalue'

        v = self.rawCreateValueFromAddress(name, addr, ty)
        if v.IsValid():
            if QUIRK_100103405 and not addr:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v

        lldbwrap_raise(ValueError, self.CreateValueFromAddress, None, name)


@six.add_metaclass(LLDBWrapMetaclass)
class SBType(lldb.SBType):

    #
    # Extensions
    #

    def _findFieldOffsetByName(self, name):
        """ internal helper """

        for idx in range(self.GetNumberOfFields()):
            field = self.GetFieldAtIndex(idx)
            fname = field.GetName()

            if fname == name:
                return field.GetOffsetInBytes(), field.GetType()

            if fname is None:
                offs, ty = field.GetType()._findFieldOffsetByName(name)
                if offs is not None:
                    return offs + field.GetOffsetInBytes(), ty

        return None, None

    def _findFieldOffsetByPath(self, path):
        """ internal helper """

        offs = 0
        ty   = self

        key = path[1:] if path[0] == '.' else path

        while key != '':
            name, _, key = key.partition('.')
            index = None

            if name[-1] == ']':
                name, _, index = name[:-1].partition('[')
                if not index.isdigit():
                    raise KeyError("Invalid path '{}'".format(path))
                index = int(index)

            f_offs, ty = ty._findFieldOffsetByName(name)
            if f_offs is None:
                return None, None, None

            offs += f_offs
            if index is not None:
                if ty.GetTypeFlags() & lldb.eTypeIsArray:
                    ty = ty.GetArrayElementType()
                else:
                    ty = ty.GetPointeeType()
                offs += ty.GetByteSize() * index

        return offs, ty, name

    def xGetFieldOffset(self, path_or_name):
        """
        Returns offsetof(type, path_or_name) in bytes.

        @param path_or_name (str)
            The field path or name to compute the offset of

        @return (int or None)
            The requested offset of the field within the type,
            or None if the field wasn't found
        """
        return self._findFieldOffsetByPath(path_or_name)[0]

    def xContainerOfTransform(self, path):
        """
        Returns a function that can be used to apply a "__container_of"
        transformation repeatedly (by field path).

        @param path_or_name (str)
            The field path or name to compute the offset of

        @returns (function)
            A function that returns the value resulting of
            __container_of(value, type_t, path) for this type.
        """

        offs = self.xGetFieldOffset(path)

        return lambda x: x.xCreateValueFromAddress(None, x.GetLoadAddress() - offs, self)

    def xContainerOf(self, path_or_name, value):
        """ same as self.xContainerOfTransform(path_or_name)(value) """

        return self.xContainerOfTransform(path_or_name)(value)


@six.add_metaclass(LLDBWrapMetaclass)
class SBValue(lldb.SBValue):

    if QUIRK_100103405:
        @classmethod
        def xUpcast(cls, value):
            #
            # LLDB insists on trying to translate "NULL" for `void *`
            # when dynamic values are enabled. It never caches the
            # negative result which can yield really slow performance.
            #
            # Work it around by disabling dynamic values, looking at whether
            # it's vaguely looking like a pointer and its value is a NULL
            # pointer, and if not, turn dynamic values back on
            #
            # This check is extremely expensive and makes shorcuts,
            # such as testing against "8" (sizeof(void *) on LP64)
            # in order to delay realizing the type as much as possible
            #
            dyn = value.GetPreferDynamicValue()
            if dyn:
                value.SetPreferDynamicValue(0)
                if (value.GetByteSize() != 8 or
                        value.GetValueAsUnsigned() or
                        not value.TypeIsPointerType()):
                    value.SetPreferDynamicValue(dyn)

            value.__class__ = cls

    #
    # Manually written checked wrappers
    #

    @functools.wraps(lldb.SBValue.GetValueAsSigned)
    def chkGetValueAsSigned(self):
        err = lldb.SBError()
        res = self.GetValueAsSigned(err)
        if res or err.Success():
            return res
        lldbwrap_raise(ValueError, self.chkGetValueAsSigned, err.GetCString(),
            self)

    @functools.wraps(lldb.SBValue.GetValueAsUnsigned)
    def chkGetValueAsUnsigned(self):
        err = lldb.SBError()
        res = self.GetValueAsUnsigned(err)
        if res or err.Success():
            return res
        lldbwrap_raise(ValueError, self.chkGetValueAsUnsigned, err.GetCString(),
            self)

    @functools.wraps(lldb.SBValue.SetValueFromCString)
    def chkSetValueFromCString(self, value_str):
        err = lldb.SBError()
        if not self.SetValueFromCString(value_str, err):
            lldbwrap_raise(ValueError, self.chkSetValueFromCString, err.GetCString(),
                self, value_str)

    @functools.wraps(lldb.SBValue.SetData)
    def chkSetData(self, data):
        err = lldb.SBError()
        if not self.SetData(data, err):
            lldbwrap_raise(ValueError, self.chkSetData, err.GetCString(),
                self, data)

    if QUIRK_99806493:
        def Cast(self, ty):
            v = super(SBValue, self).Cast(ty)
            SBValue.xUpcast(v)

            if not v.IsValid() or not v.TypeIsPointerType():
                return v

            #
            # NULL is fine, needs no PAC stripping,
            # and it makes CreateValueFromAddress behave funny.
            #
            addr = v.GetValueAsAddress()
            if addr == 0:
                return v

            #
            # Casting from a pointer type to another
            # is not stripping __ptrauth, let's fix it
            #
            nv = v.rawCreateValueFromAddress(v.GetName(), addr, ty)
            nv.SetPreferDynamicValue(v.GetPreferDynamicValue())
            v = nv.AddressOf().Cast(ty)

            if QUIRK_100162262:
                nv = v.Persist()
                nv.SetPreferDynamicValue(v.GetPreferDynamicValue())
                v = nv

            # no need for QUIRK_100103405, can't be NULL
            v.__class__ = SBValue
            return v

        def chkCast(self, ty):
            v = self.Cast(ty)
            if v.IsValid():
                return v

            lldbwrap_raise(ValueError, SBValue.Cast, None, self, ty)

    if QUIRK_100162262:
        def AddressOf(self):
            v = super(SBValue, self).AddressOf().Persist()
            # no need for QUIRK_100103405
            v.__class__ = SBValue
            return v

        def chkAddressOf(self):
            v = self.AddressOf()
            if v.IsValid():
                return v

            lldbwrap_raise(ValueError, SBValue.AddressOf, None, self)

    if QUIRK_104494282:
        def Dereference(self):
            addr = self.GetValueAsAddress()
            if addr == self.GetValueAsUnsigned():
                v = super(SBValue, self).Dereference()
                SBValue.xUpcast(v)
                return v

            return self.xCreateValueFromAddress(self.GetName(),
                addr, self.GetType().GetPointeeType())

    if QUIRK_102642763:
        def GetChildMemberWithName(self, name):
            v = super(SBValue, self).GetChildMemberWithName(name)
            SBValue.xUpcast(v)
            if v.IsValid():
                return v

            # Emulate compiler logic and visit all nested anon struct/unions.
            if self.GetType().IsPointerType():
                return self.xDereference().GetChildMemberWithName(name)

            offs, mty = self.GetType()._findFieldOffsetByName(name)
            if offs is None:
                # LLDB returns instance of SBValue that is set as invalid.
                # Re-use the invalid one from initial lookup.
                return v

            return self.xCreateValueFromAddress(name, self.GetLoadAddress() + offs, mty)

        def GetValueForExpressionPath(self, path):
            v = super(SBValue, self).GetValueForExpressionPath(path)
            SBValue.xUpcast(v)
            if v.IsValid():
                return v

            # Emulate compiler logic and visit all nested anon struct/unions.
            if self.GetType().IsPointerType():
                return self.xDereference().GetValueForExpressionPath(path)

            # Emulate compiler logic and visit all nested anon struct/unions.
            offs, mty, name = self.GetType()._findFieldOffsetByPath(path)
            if offs is None:
                # LLDB returns instance of SBValue that is set as invalid.
                # Re-use the invalid one from initial lookup.
                return v

            return self.xCreateValueFromAddress(name, self.GetLoadAddress() + offs, mty)

    #
    # Extensions
    #

    def xCreateValueFromAddress(self, name, addr, ty):
        """
        Create an SBValue with the given name by treating the memory starting
        at addr as an entity of type.

        More tolerant wrapper around CreateValueFromAddress() that accepts
        for @c name to be None and @c addr to be an lldb.SBAddress

        @param name (str or None)
            The name of the resultant SBValue

        @param addr (int or lldb.SBAddress)
            The address of the start of the memory region to be used.

        @param ty (lldb.SBType)
            The type to use to interpret the memory starting at addr.

        @return (lldb.SBValue)
            An SBValue of the given type.

        @raises ValueError
            For various error conditions.
        """

        if isinstance(addr, lldb.SBAddress):
            addr = addr.GetLoadAddress()

        if name is None:
            # SBValue's version of CreateValueFromAddress() accepts None,
            # but let's be consistent.
            name = 'newvalue'

        return self.chkCreateValueFromAddress(name, addr, ty)

    def xGetSiblingValueAtIndex(self, index, stride=None):
        """
        Returns a sibling value to the current one in an array.

        This basically performs pointer arithmetics on the SBValue.

        @param index (int)
            The index of the element to return relative to the current one.

        @param stride (int or None):
            If specified, use this stride instead of the natural value type size.

        @returns (lldb.SBValue)
            The resulting value.
        """

        if index:
            addr = self.GetLoadAddress() + index * (stride or self.GetByteSize())
            return self.chkCreateValueFromAddress(self.GetName(), addr, self.GetType())
        return self

    def xIterSiblings(self, start, stop, step=1):
        """
        Returns an iterator for sibling value to the current one in an array.

        This basically performs pointer arithmetics on the SBValue.

        @param start (int)
            The first index (inclusive) to return

        @param stop (int)
            The last index (exclusive) to return

        @param step (int or None):
            The increment step if any

        @returns (lldb.SBValue)
            The resulting value.
        """

        size = self.GetByteSize()
        ty   = self.GetType()
        base = self.GetLoadAddress()

        # aggressively cache the data
        self.target.xReadBytes(base + start * size, (stop - start) * size)

        return (
            self.chkCreateValueFromAddress(None, base + i * size, ty)
            for i in range(start, stop, step)
        )

    def xDereference(self):
        """
        Version of Dereference() that does the right thing for flexible arrays,
        and returns None if NULL is being dereferenced.

        @returns (lldb.SBValue):
            - a reference to value[0] if value is a valid pointer/array
            - None otherwise
        """

        rawty = self.rawGetType()
        fl    = rawty.GetTypeFlags()

        if fl & lldb.eTypeIsArray:
            return self.xCreateValueFromAddress(self.GetName(),
                self.GetLoadAddress(), rawty.GetArrayElementType())

        if fl & lldb.eTypeIsPointer:
            return self.chkDereference() if self.GetValueAsAddress() else None

        lldbwrap_raise(TypeError, self.xDereference, "Type can't be dereferenced")


    def xGetValueAsScalar(self, needed=0, rejected=0):
        """
        Get the scalar value of an SBValue

        @param needed (lldb.eTypeIs* mask)
            Sets of flags that should be set or the conversion should fail.

        @param rejected (lldb.eTypeIs* mask)
            Sets of flags that should fail the conversion if set on the value.
        """

        flags = self.rawGetType().GetTypeFlags()

        if (flags & needed) != needed:
            lldbwrap_raise(ValueError, self.xGetValueAsScalar,
                "value of type {} has missing flags {:#x}".format(
                self.GetType().GetDisplayTypeName(), (flags & needed) ^ needed),
                self, needed=needed, rejected=rejected)

        if flags & rejected:
            lldbwrap_raise(ValueError, self.xGetValueAsScalar,
                "value of type {} has rejected flags {:#x}".format(
                self.GetType().GetDisplayTypeName(), flags & rejected),
                self, needed=needed, rejected=rejected)

        if flags & lldb.eTypeIsPointer:
            return self.GetValueAsAddress()

        err = lldb.SBError()
        if flags & lldb.eTypeIsSigned:
            res = self.GetValueAsSigned(err)
        else:
            res = self.GetValueAsUnsigned(err)
            if QUIRK_99785324 and res and flags & lldb.eTypeIsEnumeration:
                try:
                    if (res >> (self.GetByteSize() * 8 - 1) and
                            not self.rawAddressOf().IsValid()):
                        #
                        # This field is:
                        # - likely a bitfield (we can't take its AddressOf())
                        # - unsigned
                        # - with its top bit set
                        #
                        # This might be hitting rdar://99785324 where lldb
                        # incorrectly sign-extends unsigned bit-fields.
                        #
                        # Here comes a crime against good taste: the expression
                        # evaluator of lldb _knows_ how to do the right thing,
                        # and now that the only thing we have is this lousy
                        # lldb.SBValue(), we can only get to it via __str__().
                        #
                        # We parse something like this here:
                        #   '(type_t:12) path = 42'
                        #
                        str_value = str(self)
                        res = int(str_value[str_value.rfind(' '):], 0)
                except:
                    pass

        if six.PY2 and (flags & (lldb.eTypeIsFloat | lldb.eTypeIsComplex)) == 0:
            res = int(res)

        if res or err.Success():
            return res

        lldbwrap_raise(ValueError, self.xGetValueAsScalar, err.GetCString(),
            self, needed=needed, rejected=rejected)


    def xGetValueAsInteger(self):
        """
        Get the integer value of an SBValue (fails for floats or complex)
        """

        mask = lldb.eTypeIsFloat | lldb.eTypeIsComplex
        return self.xGetValueAsScalar(rejected=mask)


    def xGetValueAsCString(self, max_len=1024):
        """
        Gets the cstring value of an SBValue.

        @param max_len (int)
            The maximum lenght expected for that string

        @returns (str)
            A string holding the contents of the value.

        @raises TypeError
            If the value can't be converted to a string
        """

        if not self.IsValid():
            lldbwrap_raise(ValueError, self.xGetValueAsCString, "Value is invalid", self)

        return self.target.GetProcess().chkReadCStringFromMemory(self.GetValueAsAddress(), max_len)


    def xGetScalarByName(self, name):
        """ same as chkGetChildMemberWithName(name).xGetValueAsScalar() """

        v = super(SBValue, self).GetChildMemberWithName(name)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsScalar()

        lldbwrap_raise(ValueError, self.GetChildMemberWithName, None, name)

    def xGetScalarAtIndex(self, index):
        """ same as chkGetChildAtIndex(index).xGetValueAsScalar() """

        v = super(SBValue, self).GetChildAtIndex(index)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsScalar()

        lldbwrap_raise(ValueError, self.GetChildAtIndex, None, index)

    def xGetScalarByPath(self, path):
        """ same as chkGetValueForExpressionPath(path).xGetValueAsScalar() """

        v = super(SBValue, self).GetValueForExpressionPath(path)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsScalar()

        lldbwrap_raise(ValueError, self.GetValueForExpressionPath, None, path)


    def xGetIntegerByName(self, name):
        """ same as chkGetChildMemberWithName(name).xGetValueAsInteger() """

        v = super(SBValue, self).GetChildMemberWithName(name)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsInteger()

        lldbwrap_raise(ValueError, self.GetChildMemberWithName, None, name)

    def xGetIntegerAtIndex(self, index):
        """ same as chkGetChildAtIndex(index).xGetValueAsInteger() """

        v = super(SBValue, self).GetChildAtIndex(index)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsInteger()

        lldbwrap_raise(ValueError, self.GetChildAtIndex, None, index)

    def xGetIntegerByPath(self, path):
        """ same as chkGetValueForExpressionPath(path).xGetValueAsInteger() """

        v = super(SBValue, self).GetValueForExpressionPath(path)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsInteger()

        lldbwrap_raise(ValueError, self.GetValueForExpressionPath, None, path)


    def xGetPointeeByName(self, name):
        """ same as chkGetChildMemberWithName(name).xDereference() """

        v = super(SBValue, self).GetChildMemberWithName(name)
        if v.IsValid():
            SBValue.xUpcast(v)
            return v.xDereference()

        lldbwrap_raise(ValueError, self.GetChildMemberWithName, None, name)

    def xGetPointeeAtIndex(self, index):
        """ same as chkGetChildAtIndex(index).xDereference() """

        v = super(SBValue, self).GetChildAtIndex(index)
        if v.IsValid():
            SBValue.xUpcast(v)
            return v.xDereference()

        lldbwrap_raise(ValueError, self.GetChildAtIndex, None, index)

    def xGetPointeeByPath(self, path):
        """ same as chkGetValueForExpressionPath(path).xDereference() """

        v = super(SBValue, self).GetValueForExpressionPath(path)
        if v.IsValid():
            SBValue.xUpcast(v)
            return v.xDereference()

        lldbwrap_raise(ValueError, self.GetValueForExpressionPath, None, path)


    def xGetLoadAddressByName(self, name):
        """ same as chkGetChildMemberWithName(name).GetLoadAddress() """

        v = super(SBValue, self).GetChildMemberWithName(name)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            return v.GetLoadAddress()

        lldbwrap_raise(ValueError, self.GetChildMemberWithName, None, name)

    def xGetLoadAddressAtIndex(self, index):
        """ same as chkGetChildAtIndex(index).GetLoadAddress() """

        v = super(SBValue, self).GetChildAtIndex(index)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            return v.GetLoadAddress()

        lldbwrap_raise(ValueError, self.GetChildAtIndex, None, index)

    def xGetLoadAddressByPath(self, path):
        """ same as chkGetValueForExpressionPath(path).GetLoadAddress() """

        v = super(SBValue, self).GetValueForExpressionPath(path)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            return v.GetLoadAddress()

        lldbwrap_raise(ValueError, self.GetValueForExpressionPath, None, path)


    def xGetCStringByName(self, name, *args):
        """ same as chkGetChildMemberWithName(name).xGetValueAsCString() """

        v = super(SBValue, self).GetChildMemberWithName(name)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsCString(*args)

        lldbwrap_raise(ValueError, self.GetChildMemberWithName, None, name)

    def xGetCStringAtIndex(self, index, *args):
        """ same as chkGetChildAtIndex(index).xGetValueAsCString() """

        v = super(SBValue, self).GetChildAtIndex(index)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsCString(*args)

        lldbwrap_raise(ValueError, self.GetChildAtIndex, None, index)

    def xGetCStringByPath(self, path, *args):
        """ same as chkGetValueForExpressionPath(path).xGetValueAsCString() """

        v = super(SBValue, self).GetValueForExpressionPath(path)
        if v.IsValid():
            if QUIRK_100103405:
                v.SetPreferDynamicValue(0)
            v.__class__ = SBValue
            return v.xGetValueAsCString(*args)

        lldbwrap_raise(ValueError, self.GetValueForExpressionPath, None, path)


def GetDebugger():
    """ Alternative to lldb.debugger since we can't hook globals """
    return SBDebugger(lldb.debugger)

def GetTarget():
    """
    Alternative to lldb.target

    Using lldb.target has several issues because it is set late by lldb,
    and might resolve to None even when there is a selected target already.
    """

    return GetDebugger().GetSelectedTarget()

def GetProcess():
    """
    Alternative to lldb.process

    Using lldb.process has several issues because it is set late by lldb,
    and might resolve to None even when there is a selected target already.
    """
    return GetTarget().GetProcess()

__all__.extend((
    GetDebugger.__name__,
    GetProcess.__name__,
    GetTarget.__name__,
))


################################################################################
#
# Code to generate the module content by replicating `lldb`
#

def lldbwrap_generate(this_module):
    sb_classes = (
       m
       for m in inspect.getmembers(lldb, inspect.isclass)
       if m[0][:2] == "SB"
    )

    for name, base in sb_classes:
        cls = getattr(this_module, name, None)
        if not hasattr(this_module, name):
            attr = {}
            lldbwrap_update_class_dict(name, base, attr)
            cls = type(name, (base,), attr)
            setattr(this_module, name, cls)

        UPCASTS[base] = cls.xUpcast
        __all__.append(name)

    #
    # Re-export globals
    #

    for name, value in inspect.getmembers(lldb):

        if name.startswith("LLDB_"):
            setattr(this_module, name, value)
            __all__.append(name)
            continue

        if name[0] == 'e' and name[1].isupper():
            setattr(this_module, name, value)
            __all__.append(name)
            continue

lldbwrap_generate(sys.modules[__name__])
